/**
  * class CR
  *
  * The main Character Rigger Data Set. This class is the backbone of the
  * Character Rigger script. Having everything within this class allows for
  * a more secure namespace and better organization.
  */
CR = function (scriptName, version, global_this)
{
    this._init (scriptName, version, global_this);
    this.scriptName = "CR.jsx";
}



/**
 * _init sets all CR attributes to their default value. Make sure to call this
 * method within your class constructor
 */
CR.prototype._init = function (scriptName, version, global_this)
{
    /**
     * The Character Rigger User Interface
     */
    this.UI = new CRUI(global_this, this);
    
    /**
     * An array of the characters defined. This array will contain an object
     * that allows the storage of the Character object and its id for reference
     */
    this.characters = new Array();

    /**
     * The current composition we are opperating in.
     */
    this.currentComp = "";

    /**
     * The version of this script.
     */
    this.version = version;

    /**
     * The name of this script.
     */
    this.scriptName = scriptName;

    /**
     * An array of error objects
     */
    this.errors = new Array();

    /**
     * The help documentation string.
     */
    this.helpText = "";

    /**
     * The current Project
     */
    this.project = null;

    /**
     * Whether or not to output debug messages.
     */
    this.debug = false;

    /**
     *UI Functions controlled by the CR object
     */

}

/**
 * Builds the UI for the Character Rigger Class and sets up the UI functions it can
 */
CR.prototype.buildUI = function ()
{
    CR.UI.show();
}


/**
 * Resets all of the variables.
 */
CR.prototype.resetVars = function ()
{

}


/**
 * Displays help information
 */
CR.prototype.help = function ()
{

}


/**
 *Alerts the user of an error.
 *@param {CR_Error} theError The CR_Error object to raise
 */
CR.prototype.raiseError = function (theError)
{
    eWindow = new Window("dialog", "Character Rigger Error", undefined, {resizeable:false});

    eWindow.grp = eWindow.add("group { orientation: 'column', margins:0, alignment:['fill','fill'], size: [350, 450], \
                                    err: EditText {properties: {multiline:true}, alignment: ['fill', 'fill'], size: [350,430]},\
                                    b: Button {text: 'Ok'}\
                                }");
    eWindow.grp.b.onClick = function() { eWindow.close(); }
    eWindow.center();
    eWindow.grp.err.text = theError.toString();
    eWindow.show();
}


/**
 * Refreshes the values of the current project and composition. It then scans
 * through the layers within the composition to try and rebuild itself based upon
 * the comments within each layer.
 */
CR.prototype.refresh = function ()
{

}

/**
 * @param {String} message The message to be output to if debug is set.
 */
//CR.prototype.outputDebug = function (message)
//{
//    if(this.debug == true)
//    {
//        $.writeLn(message);
//    }
//}

/**
 *Directs the onClick event of the save Character button to either edit or add
 *a character
 *@param {String} name
 *@param {Int} id
 */
//CR.prototype.handleCharacterInput = function(name)
//{
//    //Determine what state the UI is in
//    if(this.UI.state == this.UI.states.ADDING)
//    {
//        //Add the character
//        this.addCharacter(name);
//    }else if (this.UI.state == this.UI.states.EDITING){
//        //Edit the character
//        this.editCharacter(name, this.UI.currentlyEditing.objId);
//    }
//}

/**
 * Adds a new Character
 * @param {String} name The name of the character
 */
CR.prototype.addCharacter = function (name)
{
    //Create the Character Object
    try{
        //We must first make sure there is an active composition
        var activeComp = this.getComp();
        if(activeComp !== false)
        {
            debug("Adding Character: " + name);

            app.beginUndoGroup("Add Character: " + name);
            var newId = this.generateId();

            //Create the new Character Object and place it in the composition
            this.characters[this.characters.length] = new Character(name, newId, this.getComp());
            
            debug("--Character Object Created");
            //Add this character to the UI
            this.UI.addCharacter(name, newId);

            //Clear the Character Name edit text field
            this.UI.clearCharacterNameToSave();

            debug("--Character added to UI");
            app.endUndoGroup();
        }else{
            alert(errors.NO_ACTIVE_COMP);
        }
    }catch(e){
        debug("--Error");
        this.raiseError(e);
        //If the character could not be added, make sure to close the undo group
        app.endUndoGroup();
    }
    debug("--Done");
}

/**
 *Edits the character based upon the new name given by the UI
 *@param {String} name The new name for the character
 *@param {Int} id The Character's unique id number
 */
CR.prototype.editCharacter = function(name, id)
{
    //Modify the character object
    try
    {
        debug("Editing Character");
        app.beginUndoGroup("Rename Character To: " + name);
        var characterArrayId = this.getCharacterArrayId(id);
        //Change the name of the character
        this.characters[characterArrayId].rename(name);

        debug("--Character Renamed: " + name);
        //Update the UI Tree
        this.UI.renameCharacter(id, name);

        debug("--Character's Tree Item Renamed");
        app.endUndoGroup();
    }catch(e){
        debug("--Error");
        this.raiseError(e);

        //If the character could not be edited, make sure to close the undo group
        app.endUndoGroup();
    }
    debug("--Done");
}

/**
 * Adds a BodyPart to a specified Character
 * @param {Character} character The character to add the Body Part to
 * @param {BodyPart} BP
 */
CR.prototype.addBodyPart = function (character, bp) {

}

/**
 * Get the active composition
 */
CR.prototype.getComp = function (){
    if( app.project.activeItem != null
        && app.project.activeItem instanceof CompItem)
    {
        return app.project.activeItem;
    }else{
        return false;
    }
        
}

/**
 *Gets the character object based upon its id
 *@param {Int} id The id of the character to return.
 *@return {Character | Bool} The character that matches this id or false
 *for no character found
 */
CR.prototype.getCharacterById = function(id)
{
    debug("Getting Character By Id: " + id);

    var i = this.characters.length-1; //Subtract one since the array is zero-based.
    while(i>=0)
    {
        debug(this.characters[i].toString());
        if(this.characters[i].id == id)
        {
            debug("Returning Character: " + this.characters[i].toString());
            return this.characters[i];
        }
        i--;
    }
    debug("--No Character Found");
    return false;
}

/**
 *Gets the positional id for the character in the CR characters array
 *@param {Int} id The id of the character to return.
 *@return {Int | Bool} The positional id of the character OR false if the
 *character id could not be found.
 */
CR.prototype.getCharacterArrayId = function(id)
{
    debug("Getting Character by Array Id: " + id);
    var i = this.characters.length-1; //Subtract one since the array is zero based
    while(i>=0)
    {
        if(this.characters[i].id == id)
        {
            debug("-- Returning Id: " + i);
            return i;
        }
        i--;
    }
    debug("--No Id found");
    return false;
}

/**
 * Generates an integer based upon the amount of time since the epoch for use
 * as an ID
 */
CR.prototype.generateId = function(){
    debug("Generating Id");
    id = new Date;
    newId = id.getTime();
    debug("ID: " + newId)
    return newId;
}